`timescale 1ns / 1ps
`include "../include/cpu_defines.sv"
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/12 15:17:09
// Design Name: 
// Module Name: FrontEnd
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module FrontEnd(
	input logic cpu_clk,
	input logic cpu_rst_n,
	input logic cp0_fail,
	input logic branch_fail,
	input logic use_branch_addr,
	input logic btb_clear, // 碰到tlb_refill tlb_invalid时需要清空btb
	input logic stall,

	input logic [1: 0][31: 0] inst_i,
	input logic [1: 0] inst_valid_i,
	input logic [1: 0][31: 0] inst_request_addr,
	input logic inst_addr_ok,
	input logic inst_data_ok,
	// input logic inst_accept,
	input logic tlb_refill,
	input logic tlb_invalid,

	// 分支预测相关信号
	input logic dis_branch_en, // 需要分支预测的指令
	input logic dis_j_en, // 所有包含延迟槽的指令，包括j,jal等
	input logic [4: 0] dis_rob_index,
	input logic [31: 0] dis_analyze_addr,
	input logic wb_branch_en1,
	input logic wb_branch_en2,
	input logic [`BRANCH_BUFFER_WIDTH-1: 0] wb_branch_index1,
	input logic [`BRANCH_BUFFER_WIDTH-1: 0] wb_branch_index2,
	input logic [4: 0] wb_rob_index1,
	input logic [4: 0] wb_rob_index2,
	input logic [31: 0] wb_branch1,
	input logic [31: 0] wb_branch2,
	input logic branch_retire_en,
	input logic retire_branch,
	input logic [5: 0] branch_retire_rob_index,
	input logic [31: 0] cp0_addr,
	input logic [1: 0] ras_up,
	input logic [1: 0] ras_down,

	output logic [`BRANCH_BUFFER_WIDTH-1: 0] branch_index,
	output logic branch_error1,
	output logic branch_error2,

	output logic inst_req,
	output logic inst_clear,
	output logic [1: 0] inst_size,
	output logic [31: 0] inst_addr,

	output logic [1: 0] ien_o,
	output logic [1: 0][31: 0] inst_o,
	output logic [1: 0][31: 0] iaddr_o,
	output logic [1: 0] tlb_refill_o,
	output logic [1: 0] tlb_invalid_o,
	output logic [1: 0] issue_num,
	output logic [31: 0] analyze_addr
    );

	logic [31: 0] pc, next_pc;
	logic [31: 0] pc_normal_next; // 不跳转情况下下一条地址
	logic [31: 0] branch_addr;
	logic [31: 0] branch_fail_addr;
	logic [31: 0] delay_addr;
	logic branch_need_jump, branch_predict;
	logic clear, front_stall;
	logic [1: 0] inst_valid;
	logic [1: 0] inst_buffer_en;
	logic [1: 0] issue_num_pre;
	logic [1: 0] request_inst_num;
	logic request_success;
	logic inst_buffer_full;
	logic next_cp0_fail, next_branch_fail;
	logic front_clear_pre, front_clear; // 跳转时清空
	logic [1: 0] analyze_result;
	logic ras_re, ras_we, branch_jalr;

	// p表示从分支预测器中传出的，i表示从指令缓存中传出的
	BranchInfo inst_branch_info_p, inst_branch_info_i;

	logic branch_full;
	logic branch_store_en;
	logic [31: 0] branch_current_addr;
	logic [31: 0] analyze_addr1, analyze_addr2;
	logic [31: 0] jump_addr1, signed_imm1, branch_addr1;
	logic [31: 0] jump_addr2, signed_imm2, branch_addr2;
	InstInfoBundle inst_info_bundle1, inst_info_bundle2;

	// 处理延迟槽
	typedef enum  { DELAY_IDLE, DELAY_WAIT, DELAY } DelayState;
	logic delay_transient_cond;
	DelayState delay_state;

	logic btb_en, btb_delay, btb_delay_transient, btb_delay_jump;
	logic [31: 0] btb_delay_addr, btb_addr;
	typedef enum {BTB_IDLE, BTB_DELAY} BTBState_t;
	BTBState_t btb_state;

	assign pc_normal_next = (pc + 4'b1000) & (pc[4] && pc[3] ? 32'hfffffff0 : 32'hfffffffc);
	// 如果发射两条指令并且第一条指令需要跳转
	assign branch_need_jump = (issue_num == 2'b10) && (inst_info_bundle1.analyze_result[1] && !inst_branch_info_i.btb_en || (inst_info_bundle1.analyze_result == 2'b01 && branch_predict));
	assign inst_addr = pc;
	assign inst_size = btb_delay_jump ? 2'b01 : 2'b10;
	assign request_success = inst_addr_ok && inst_req;
	assign next_pc = cp0_fail && !use_branch_addr ? cp0_addr : // 异常跳转
					branch_fail || use_branch_addr ? branch_fail_addr : // 分支预测失败跳转
					delay_state == DELAY_WAIT && delay_transient_cond ? delay_addr : // 延迟槽指令发射后跳转
					branch_need_jump ? branch_addr : // 正常的分支跳转
					btb_delay_jump ? btb_delay_addr :
					btb_en && !btb_delay_transient && request_success ? btb_addr :
					request_success ? pc_normal_next : pc; // 正常情况下取指
	always_ff @(posedge cpu_clk)begin
		next_cp0_fail <= cp0_fail;
		next_branch_fail <= branch_fail;
		inst_req <= cpu_rst_n && ((!inst_buffer_full || front_clear_pre) || cp0_fail || branch_fail);
		inst_clear <= cp0_fail | branch_fail | (delay_state == DELAY_WAIT && delay_transient_cond) | branch_need_jump;
		clear <= cp0_fail | branch_fail;
		if(cpu_rst_n == 1'b0)begin
			pc <= `PC_INIT;
		end
		else begin
			pc <= next_pc;
		end
	end
	assign inst_valid = inst_valid_i;

	assign delay_transient_cond = issue_num != 2'b00;
	assign btb_delay_transient = (pc[4] & pc[3] & pc[2]) | (~btb_delay & pc[2]) | (btb_delay & ~pc[2]);
	assign btb_delay_jump = btb_state == BTB_DELAY && !(clear || front_clear) && request_success;
	always_ff @(posedge cpu_clk)begin
		if(cpu_rst_n == 1'b0 || clear)begin
			delay_state <= DELAY_IDLE;
			delay_addr <= 0;
		end
		else begin
			case(delay_state)
			DELAY_IDLE:begin
				if(issue_num == 2'b01 && (inst_info_bundle1.analyze_result == 2'b01 && branch_predict || inst_info_bundle1.analyze_result[1] && !inst_branch_info_i.btb_en) || issue_num == 2'b10 && (inst_info_bundle2.analyze_result == 2'b01 && branch_predict || inst_info_bundle2.analyze_result[1] && !inst_branch_info_i.btb_en))begin
					delay_state <= DELAY_WAIT;
					delay_addr <= branch_addr;
				end
			end
			DELAY_WAIT:begin
				if(delay_transient_cond)begin
					delay_state <= DELAY;
				end
			end
			DELAY:begin
				delay_state <= DELAY_IDLE;
			end
			endcase
		end
	end

	always_ff @(posedge cpu_clk)begin
		if(cpu_rst_n == 1'b0)begin
			btb_state <= BTB_IDLE;
			btb_delay_addr <= 0;
		end
		else begin
			case(btb_state)
			BTB_IDLE:begin
				if(btb_en && btb_delay_transient && request_success)begin
					btb_state <= BTB_DELAY;
					btb_delay_addr <= btb_addr;
				end
			end
			BTB_DELAY:begin
				if((clear || front_clear) && btb_en && btb_delay_transient && request_success)begin
					btb_delay_addr <= btb_addr;
				end
				else if(clear || front_clear || request_success)begin
					btb_state <= BTB_IDLE;
				end
			end
			endcase
		end
	end

	assign branch_store_en = delay_state == DELAY_IDLE && !front_clear && (issue_num != 2'b00 && inst_info_bundle1.analyze_result[0] || issue_num == 2'b10 && inst_info_bundle2.analyze_result[0]);
	assign ras_re = delay_state == DELAY_IDLE && !front_clear && (issue_num != 2'b00 && inst_info_bundle1.ras_re || issue_num == 2'b10 && inst_info_bundle2.ras_re);
	assign ras_we = delay_state == DELAY_IDLE && !front_clear && (issue_num != 2'b00 && inst_info_bundle1.ras_we || issue_num == 2'b10 && inst_info_bundle2.ras_we);
	// assign branch_store_en[0] = ien_o[0] & (inst_info_bundle1.analyze_result == 2'b01 || inst_info_bundle1.analyze_result == 2'b11) && delay_state == DELAY_IDLE && ~front_clear;
	// assign branch_store_en[1] = ien_o[1] & (inst_info_bundle2.analyze_result == 2'b01 || inst_info_bundle2.analyze_result == 2'b11) && delay_state == DELAY_IDLE && ~front_clear;
	assign branch_current_addr = inst_info_bundle1.analyze_result != 2'b00 ? iaddr_o[0] : iaddr_o[1];
	assign analyze_result = inst_info_bundle1.analyze_result != 2'b00 ? inst_info_bundle1.analyze_result : inst_info_bundle2.analyze_result;

	assign jump_addr1 = {iaddr_o[0][31: 28], inst_o[0][25: 0], 2'b00};
	assign signed_imm1 = {{14{inst_o[0][15]}}, inst_o[0][15: 0], 2'b00};
	assign branch_addr1 = signed_imm1 + iaddr_o[0] + 32'b100;
	assign analyze_addr1 = inst_info_bundle1.analyze_result == 2'b10 ? jump_addr1 : branch_addr1;
	assign jump_addr2 = {iaddr_o[1][31: 28], inst_o[1][25: 0], 2'b00};
	assign signed_imm2 = {{14{inst_o[1][15]}}, inst_o[1][15: 0], 2'b00};
	assign branch_addr2 = signed_imm2 + iaddr_o[1] + 32'b100;
	assign analyze_addr2 = inst_info_bundle2.analyze_result == 2'b10 ? jump_addr2 : branch_addr2;
	assign analyze_addr = inst_info_bundle1.analyze_result != 2'b00 ? analyze_addr1 : analyze_addr2;

	assign ien_o[0] = inst_buffer_en[0] & ~front_clear & ~branch_full;
	assign ien_o[1] = inst_buffer_en[1] & ~front_clear & ~branch_full & (~(issue_num_pre == 2'b10 && delay_state == DELAY_WAIT));
	assign issue_num = stall || front_clear || clear || branch_full ? 2'b00 : delay_state == DELAY_WAIT && issue_num_pre == 2'b10 ? 2'b01 : issue_num_pre;
	assign front_clear_pre = issue_num == 2'b10 && (inst_info_bundle1.analyze_result == 2'b01 && branch_predict || inst_info_bundle1.analyze_result[1] && !inst_branch_info_i.btb_en) || delay_state == DELAY_WAIT && delay_transient_cond;

	assign front_stall = inst_buffer_full;
	always_ff @(posedge cpu_clk)begin
		front_clear <= front_clear_pre;
		request_inst_num <= cpu_rst_n == 1'b0 ? 0 : request_success ? (next_pc[4: 2] != 3'b111 ? inst_size : 2'b01) : inst_data_ok ? 0 : request_inst_num;
	end

	assign branch_jalr = inst_info_bundle1.analyze_result != 2'b00 ? inst_info_bundle1.jalr : inst_info_bundle2.jalr;
	BranchPredictor branch_predictor(
		.cpu_clk(cpu_clk),
		.cpu_rst_n(cpu_rst_n),
		.clear(clear),
		.front_clear(front_clear),
		.btb_clear(btb_clear),
		.stall(stall),
		.pc(pc),
		.pc_next(pc_normal_next),
		.request_success(request_success),
		.branch_addr(branch_addr),
		.branch_fail_addr(branch_fail_addr),
		.inst_branch_info_o(inst_branch_info_p),
		.inst_branch_info_i(inst_branch_info_i),
		.store_en(branch_store_en),
		.jalr(branch_jalr),
		.analyze_result(analyze_result),
		.analyze_addr(analyze_addr),
		.current_addr(branch_current_addr),
		.full(branch_full),
		.predict(branch_predict),
		.dis_branch_en(dis_branch_en),
		.wb_branch_en1(wb_branch_en1),
		.wb_branch_en2(wb_branch_en2),
		.wb_branch_index1(wb_branch_index1),
		.wb_branch_index2(wb_branch_index2),
		.wb_branch1(wb_branch1),
		.wb_branch2(wb_branch2),
		.retire_en(branch_retire_en),
		.retire_branch(retire_branch),
		.branch_fail(branch_fail),
		.branch_index(branch_index),
		.branch_error1(branch_error1),
		.branch_error2(branch_error2),
		.ras_re(ras_re),
		.ras_we(ras_we),
		.ras_up(ras_up),
		.ras_down(ras_down),
		.btb_en(btb_en),
		.btb_delay(btb_delay),
		.btb_addr(btb_addr),
		.dis_j_en(dis_j_en),
		.dis_rob_index(dis_rob_index),
		.dis_analyze_addr(dis_analyze_addr),
		.wb_rob_index1(wb_rob_index1),
		.wb_rob_index2(wb_rob_index2),
		.retire_rob_index(branch_retire_rob_index)
	);

	InstBuffer1 inst_buffer1(
		.cpu_clk(cpu_clk),
		.cpu_rst_n(cpu_rst_n),
		.clear(clear),
		.front_clear(front_clear),
		.stall(stall),
		.inst_i(inst_i),
		.inst_valid_i(inst_valid_i),
		.inst_request_addr(inst_request_addr),
		.inst_data_ok(inst_data_ok),
		.issue_num(issue_num),
		.issue_num_pre(issue_num_pre),
		.inst_en(inst_buffer_en),
		.inst1(inst_o[0]),
		.inst2(inst_o[1]),
		.iaddr1(iaddr_o[0]),
		.iaddr2(iaddr_o[1]),
		.inst_branch_info_i(inst_branch_info_p),
		.inst_branch_info_o(inst_branch_info_i),
		.inst_buffer_full(inst_buffer_full),
		.inst_info_bundle1(inst_info_bundle1),
		.inst_info_bundle2(inst_info_bundle2),
		.tlb_refill_i(tlb_refill),
		.tlb_invalid_i(tlb_invalid),
		.tlb_refill_o(tlb_refill_o),
		.tlb_invalid_o(tlb_invalid_o),
		.request_inst_num((request_inst_num & {2{~inst_clear}}))
	);
endmodule
